package com.wsjzzcbq.qianyuan.sys.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wsjzzcbq.qianyuan.common.bean.R;
import com.wsjzzcbq.qianyuan.common.constant.MsgConsts;
import com.wsjzzcbq.qianyuan.sys.bean.Menu;
import com.wsjzzcbq.qianyuan.sys.mapper.MenuMapper;
import com.wsjzzcbq.qianyuan.sys.param.FindMenuParam;
import com.wsjzzcbq.qianyuan.sys.param.MenuParam;
import com.wsjzzcbq.qianyuan.sys.service.MenuService;
import com.wsjzzcbq.qianyuan.sys.vo.MenuVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * MenuServiceImpl
 *
 * @author wsjz
 * @date 2022/03/10
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    @Override
    public JSONArray menu() {
        List<Menu> list = this.list();
        return menu(list);
    }

    /**
     * 递归排序 JSONArray
     * @param array
     */
    private void recursionSortJSONArray(JSONArray array) {
        array.sort(Comparator.comparing(v -> ((JSONObject)v).getInteger("sort")));
        array.forEach(a -> {
            JSONArray children = ((JSONObject)a).getJSONArray("children");
            if (children != null && !children.isEmpty()) {
                recursionSortJSONArray(children);
            }
        });
    }

    @Override
    public JSONArray menu(List<Menu> list) {
        JSONArray array = new JSONArray();

        for (Menu menu : list) {
            if (findJSONObject(menu.getId(), array) == null) {
                //当前节点不在array中，才进行添加
                createJsonObj(menu, list, array);
            }
        }
        //对array递归排序
        recursionSortJSONArray(array);
        return array;
    }

    /**
     * 创建节点核心方法
     * @param menu
     * @param list
     * @param array
     * @return
     */
    private JSONObject createJsonObj(Menu menu, List<Menu> list, JSONArray array) {
        if (menu.getParentId() != null) {
            JSONObject parent = findJSONObject(menu.getParentId(), array);
            if (parent == null) {
                //初次创建节点的情况
                //创建父节点
                Menu parentMenu = getParent(menu.getParentId(), list);
                if (parentMenu != null) {
                    JSONObject p = createJsonObj(parentMenu, list, array);
                    if (p != null) {
                        //创建当前节点
                        JSONObject cur = createJSONObjectByMenu(menu);
                        //挂到父节点
                        JSONArray children = new JSONArray();
                        children.add(cur);
                        p.put("children", children);
                        //返回当前节点作为下一级的父节点
                        return cur;
                    } else {
                        return null;
                    }

                } else {
                    //不挂到array上
                    return null;
                }

            } else {
                //把当前节点挂到parent上，先判断当前节点是否已经挂上父节点
                if (findJSONObject(menu.getId(), array) == null) {
                    //创建当前节点
                    JSONObject curObj = createJSONObjectByMenu(menu);
                    //挂到parent上
                    if (parent.getJSONArray("children") != null) {
                        parent.getJSONArray("children").add(curObj);
                    } else {
                        JSONArray children = new JSONArray();
                        children.add(curObj);
                        parent.put("children", children);
                    }
                    //返回当前节点作为下一级的父节点
                    return curObj;
                }

            }
        }

        //已经创建则返回
        JSONObject tmp = findJSONObject(menu.getId(), array);
        if (tmp != null) {
            //已经存在可以不做任何处理,返回是为了保证递归的完整性
            return tmp;
        }
        //没有则创建
        JSONObject dan = createJSONObjectByMenu(menu);
        array.add(dan);
        return dan;
    }

    /**
     * 通过 id在 array中查找节点
     * @param id
     * @param array
     * @return
     */
    private JSONObject findJSONObject(Long id, JSONArray array) {
        for (Object obj : array) {
            JSONObject jsonObj = (JSONObject)obj;
            if (jsonObj.getLong("id").equals(id)) {
                return jsonObj;
            } else {
                JSONArray children = jsonObj.getJSONArray("children");
                if (children != null && !children.isEmpty()) {
                    JSONObject jsonObject = findJSONObject(id, children);
                    if (jsonObject != null) {
                        //只有此处不为空才返回，为空继续下次循环
                        return jsonObject;
                    }
                }
            }
        }
        //所有都没找到才返回空
        return null;
    }

    private Menu getParent(Long parentId, List<Menu> list) {
        for (Menu m : list) {
            if (parentId.equals(m.getId())) {
                return m;
            }
        }
        return null;
    }

    /**
     * 创建节点
     * @param menu
     * @return
     */
    private JSONObject createJSONObjectByMenu(Menu menu) {
        JSONObject json = new JSONObject();

        if (StringUtils.isNotBlank(menu.getName())) {
            json.put("name", menu.getName());
        }
        if (StringUtils.isNotBlank(menu.getComponent())) {
            json.put("component", menu.getComponent());
        }
        if (StringUtils.isNotBlank(menu.getPath())) {
            json.put("path", menu.getPath());
        }
        if (StringUtils.isNotBlank(menu.getRedirect())) {
            json.put("redirect", menu.getRedirect());
        }
        if (menu.getHidden() != null) {
            json.put("hidden", menu.getHidden());
        }
        if (menu.getSort() != null) {
            json.put("sort", menu.getSort());
        }

        JSONObject meta = new JSONObject();
        meta.put("title", menu.getTitle());
        if (StringUtils.isNotBlank(menu.getIcon())) {
            meta.put("icon", menu.getIcon());
        }
        json.put("meta", meta);
        json.put("id", menu.getId());
        json.put("type", menu.getType());

        return json;
    }

    @Override
    public JSONArray menuPermission() {
        JSONArray array = new JSONArray();
        JSONArray menus = this.menu();
        copyMenu(menus, array);

        return array;
    }

    /**
     * 拷贝JSONArray
     * @param source
     * @param target
     */
    private void copyMenu(JSONArray source, JSONArray target) {
        for (Object obj : source) {
            JSONObject soutceJson = (JSONObject)obj;
            JSONObject targetJson = new JSONObject();
            //将long转成string防止js经度丢失
            targetJson.put("id", String.valueOf(soutceJson.getLong("id")));
            targetJson.put("label", soutceJson.getJSONObject("meta").getString("title"));

            target.add(targetJson);

            JSONArray children = soutceJson.getJSONArray("children");
            if (children != null && !children.isEmpty()) {
                JSONArray copyChildren = new JSONArray();
                copyMenu(children, copyChildren);
                targetJson.put("children", copyChildren);
            }
        }
    }

    @Override
    public List<Menu> getAllUserMenuByCheckedMenuIds(List<Long> checkMenuIds) {
        List<Menu> list = new LinkedList<>();
        List<Menu> menus = this.list();

        for (Long id : checkMenuIds) {
            findUserAllMenu(menus, id, list);
        }

        return list;
    }

    private void findUserAllMenu(List<Menu> menus, Long id, List<Menu> list) {

        for (Menu menu : menus) {
            if (menu.getId().equals(id)) {
                if (!list.contains(menu)) {
                    list.add(menu);
                }
                if (menu.getParentId() != null) {
                    findUserAllMenu(menus, menu.getParentId(), list);
                }
            }
        }

    }

    @Override
    public R<List<MenuVo>> findMenuList(FindMenuParam param) {
        JSONArray menus = menu();
        List<MenuVo> list = new LinkedList<>();
        copyMenuToList(menus, list);
        //模糊查询
        if (StringUtils.isNotBlank(param.getTitle())) {
            Iterator<MenuVo> iterator = list.iterator();
            while (iterator.hasNext()) {
                MenuVo vo = searchTitle(iterator.next(), param.getTitle());
                if (vo == null) {
                    iterator.remove();
                }
            }
        }
        list.sort(Comparator.comparing(MenuVo::getSort));
        return R.success(list);
    }

    private MenuVo searchTitle(MenuVo menuVo, String title) {
        if (has(menuVo.getTitle(), title)) {
            return menuVo;
        } else {
            for (MenuVo vo : menuVo.getChildren()) {
                MenuVo chilMenu = searchTitle(vo, title);
                if (chilMenu != null) {
                    return menuVo;
                }
            }
        }
        return null;
    }

    private boolean has(String title, String key) {
        return title.contains(key);
    }


    /**
     * 拷贝菜单数据到 List<MenuVo>
     * @param source
     * @param list
     */
    private void copyMenuToList(JSONArray source, List<MenuVo> list) {
        for (Object obj : source) {
            JSONObject soutceJson = (JSONObject)obj;
            MenuVo menuVo = cretaMenuVo(soutceJson);
            list.add(menuVo);

            JSONArray children = soutceJson.getJSONArray("children");
            if (children != null && !children.isEmpty()) {
                copyMenuToList(children, menuVo.getChildren());
            }
        }
    }

    private MenuVo cretaMenuVo(JSONObject json) {
        MenuVo vo = new MenuVo();
        vo.setId(String.valueOf(json.getLong("id")));
        vo.setTitle(json.getJSONObject("meta").getString("title"));
        vo.setName(json.getString("name"));
        vo.setPath(json.getString("path"));
        vo.setComponent(json.getString("component"));
        vo.setType(json.getInteger("type"));

        if (StringUtils.isNotBlank(json.getJSONObject("meta").getString("icon"))) {
            vo.setIcon(json.getJSONObject("meta").getString("icon"));
        }
        if (StringUtils.isNotBlank(json.getString("redirect"))) {
            vo.setRedirect(json.getString("redirect"));
        }
        if (StringUtils.isNotBlank(json.getString("hidden"))) {
            vo.setHidden(json.getBoolean("hidden"));
        }
        if (json.getInteger("sort") != null) {
            vo.setSort(json.getInteger("sort"));
        }

        return vo;
    }


    @Override
    public R<?> addOrUpdate(MenuParam param) {
        Menu menu = new Menu();
        BeanUtils.copyProperties(param, menu);
        if (StringUtils.isNotBlank(param.getParentId())) {
            menu.setParentId(Long.valueOf(param.getParentId()));
        }
        if (StringUtils.isBlank(param.getId())) {
            //添加
            this.save(menu);
            return R.success(MsgConsts.ADD_SUCCESS);
        } else {
            //修改
            menu.setId(Long.valueOf(param.getId()));
            this.updateById(menu);
            return R.success(MsgConsts.UPDATE_SUCCESS);
        }
    }

    @Override
    public R<MenuVo> findMenuVoById(String id) {
        if (StringUtils.isBlank(id)) {
            return R.error(MsgConsts.ID_NOT_NULL);
        }
        Menu menu = this.getById(id);
        if (menu == null) {
            return R.error(MsgConsts.ID_ERROR);
        }
        MenuVo vo = new MenuVo();
        BeanUtils.copyProperties(menu, vo);
        vo.setId(String.valueOf(menu.getId()));
        if (menu.getParentId() != null) {
            vo.setParentId(String.valueOf(menu.getParentId()));
        }
        return R.success(vo);
    }

    @Override
    public R<?> deleteMenuById(String id) {
        if (StringUtils.isBlank(id)) {
            return R.error(MsgConsts.ID_NOT_NULL);
        }
        Menu menu = this.getById(Long.valueOf(id));
        if (menu == null) {
            return R.success(MsgConsts.ID_ERROR);
        }
        List<Long> ids = new LinkedList<>();
        List<Menu> menus = this.list();
        ids.add(menu.getId());
        addChildMenuId(menu.getId(), menus, ids);
        this.removeByIds(ids);
        return R.success(MsgConsts.DELETE_SUCCESS);
    }

    /**
     * 添加全部子节点
     * @param id
     * @param menus
     * @param ids
     */
    private void addChildMenuId(Long id, List<Menu> menus, List<Long> ids) {
        for (Menu menu : menus) {
            if (menu.getParentId() != null && menu.getParentId().equals(id)) {
                ids.add(menu.getId());
                addChildMenuId(menu.getId(), menus, ids);
            }
        }
    }

}
